#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "WindowPort.h"

#include "Class.h"
#include "Error.h"
#include "System.h"
#include "WindowSystem.h"
#include "Math.h"
#include "Storage.h"
#include "String.h"
#include "Window.h"
#include "OrdColl.h"
#include "Region.h"
#include "WindowColorMap.h"
#include "Timer.h"

extern Timer *refresh;

static WindowPort *gotport;
static Token gott;

//---- WindowPort --------------------------------------------------------------

NewMetaImpl(WindowPort,Port, (TP(win), TE(state), T(lastpos), T(portrect),
																TP(wcmap)));

WindowPort::WindowPort(Window *bw, WindowType, GrCursor curs)
{
	win= bw;
	overlay= FALSE;
	state= eWsHidden;
	cursor= curs;
	hascolor= gColor;
	inval= 0;
	wcmap= 0;

	if (gWindowSystem)
		gWindowSystem->AddWindow(this);
}

WindowPort::~WindowPort()
{
	if (gWindowSystem)
		gWindowSystem->RemoveWindow(this);
	if (state != eWsClosed) {
		state= eWsClosed;
		ExitLoop();
		SafeDelete(inval);
	}
	SafeUnref(wcmap);
}

//---- Callbacks ---------------------------------------------------------------

void WindowPort::OpenNotify(const Rectangle &r)
{
	portrect= r;
	state= eWsShown;
	if (win)
		win->OpenNotify(r.extent);
}

void WindowPort::CloseNotify()
{
	SafeDelete(inval);
	state= eWsHidden;
	ExitLoop();
	if (win)
		win->CloseNotify();
	gSystem->InnerLoop();
}

void WindowPort::InputNotify(Token *t)
{
	if (state != eWsShown)
		return;

	lastpos= t->Pos;
	WindowSystem::lasttime= t->At;
		
	if (gotport == this) {
		gott= *t;
		gotport= 0;
		return;
	}

	if (WindowSystem::grabport && (WindowSystem::grabport != this)) {
		switch (t->Code) {
		case eEvtLocMove:
		case eEvtLocMoveBut:
			// ignore events
			return;
		case eEvtEnter:
		case eEvtExit:
			break;
		default:
			if (t->IsKey() || t->IsFunctionKey() || t->IsCursorKey())
				WindowSystem::grabport->InputNotify(t);
			else {
				WindowSystem::grabport->Bell(50);
				WindowSystem::grabport->Top();
			}
			return;
		}
	}
	if (t->IsMouseButton()) {
		if (WindowSystem::lastClick.DoubleClick(*t))
			WindowSystem::Clicks++;
		else
			WindowSystem::Clicks= 1;
		WindowSystem::lastClick= *t;
	}
	if (win)
		win->InputNotify(t);
	if (refresh)
		refresh->Reset();
}

//---- Invalidation ------------------------------------------------------------

void WindowPort::InvalidateRect(const Rectangle &r, bool update)
{
	//fprintf(stderr, "Inval(%d): %d %d %d %d\n", this, r.origin.x, r.origin.y,
	//                                                r.extent.x, r.extent.y);
	if (state == eWsShown) {
		if (inval == 0)
			inval= new OldRegion;
		inval->Merge(r);
	}
	if (update)
		gWindowSystem->Update();
}

void WindowPort::DevUpdate()
{
	if (inval && inval->size > 0) {
		OldRegion *old= inval;
		inval= 0;
		Rectangle *ir;
		
		for (int i= 0; ir= old->GetAt(i); i++) {
			UpdateRect(*ir);
			GiveHint(eHintUnbatch, 0, 0);
		}
		delete old;
	}
}

void WindowPort::UpdateRect(const Rectangle &r)
{
	if (win) {
		Port *oldport= GrGetPort();
		GrSetPort(this);
		InitClip();
		Clip(Inter(win->contentRect, r));
		SetPenNormal();
		SetInk(ePatBlack);
		GiveHint(eHintBatch, sizeof(Rectangle), (void*) &r);
		win->DrawAll(r, FALSE);
		FlushMyText();
		GrSetPort(oldport);
	}
}

void WindowPort::FlushColorMap()
{
	if (wcmap)
		wcmap->Install(this);
}

void WindowPort::DevSetColor(RGBColor *cp)
{
	u_long id= cp->GetId();
	if (cp->TestFlag(eInkChanged)) {
		if (cp->GetPrec() == 255) {
			if (wcmap == WindowSystem::wcmap) {
				wcmap= wcmap->MakeCopy(WindowSystem::wcmap);
				wcmap->Ref();
			}
			id= wcmap->AllocateAndSetCell(id, cp->GetRGB(), this);
		} else
			id= wcmap->RGB2Index(cp->GetRGB());
		cp->SetId(id);
		cp->ResetFlag(eInkChanged);
	}
	DevSetColor2(id);
}

//---- cursor stuff ------------------------------------------------------------

GrCursor WindowPort::SetCursor(GrCursor c)
{
	if (c != cursor && c >= eCrsNone && c <= eCrsUpDownArrow) {
		GrCursor oldc= cursor;
		cursor= c;
		DevSetCursor(c);
		return oldc;
	}
	return c;
}

GrCursor WindowPort::GetCursor()
{
	return cursor;
}

GrCursor WindowPort::SetWaitCursor(unsigned int, GrCursor c)
{
	c= SetCursor(c);
	GiveHint(eHintWinBusy, 0, 0);
	return c;
}

//---- input handling ----------------------------------------------------------

int gTimeOut;

void WindowPort::GetEvent(Token *t)
{
	gotport= this;

	gott.Code= -1;
	gott.Flags= eFlgNone;
	gott.Pos= lastpos;
	gott.At= WindowSystem::lasttime;

	if (gTimeOut >= 0) {
		if (gTimeOut < 10)
			gTimeOut= 10;
		Timer rt(gTimeOut);

		while (gotport && !rt.WasTimeout())
			gSystem->InnerLoop();
		
		if (rt.WasTimeout())
			gott.Code= -1;
	} else {
		while (gotport)
			gSystem->InnerLoop();
	}

	if (t)
		*t= gott;
	gTimeOut= -1;
}

//---- Window Management -------------------------------------------------------

void WindowPort::Hide()
{
	if (state == eWsShown) {
		FlushAnyText();
		DevHide();
	}
}

Point showdelta;

void WindowPort::Show(WindowPort *father, const Rectangle &r, bool block)
{
	Point delta;
	Rectangle rr(r);

	if (state == eWsShown)
		return;
		
	SafeDelete(inval);

	FlushAnyText();

	if (father)
		rr.origin+= father->GetRect().origin;
	else if (r.origin == gPoint_1)
		rr.origin= Half(WindowSystem::screenRect.extent - r.extent);

	if (rr.extent.x > WindowSystem::screenRect.extent.x)
		rr.extent.x= WindowSystem::screenRect.extent.x;
	if (rr.extent.y > WindowSystem::screenRect.extent.y)
		rr.extent.y= WindowSystem::screenRect.extent.y;

	delta= rr.AmountToTranslateWithin(WindowSystem::screenRect);
	rr.origin+= delta;

	DevShow(father, rr);
	showdelta= delta;
	//if (delta != gPoint0)
	//    MoveMousePos(delta);
	if (block)
		GrabLoop();
}

void WindowPort::Poll(int)
{
	WindowPort *oldgrabport= WindowSystem::grabport;
	WindowSystem::grabport= this;
	//gSystem->InnerLoop();
	gSystem->Wait(0);
	WindowSystem::grabport= oldgrabport;
}

void WindowPort::GrabLoop()
{
	exitloop= FALSE;
	WindowPort *oldgrabport= WindowSystem::grabport;
	WindowSystem::grabport= this;
	do
		gSystem->InnerLoop();
	while (! exitloop);
	WindowSystem::grabport= oldgrabport;
}

void WindowPort::Iconize()
{
	DevIconize();
}

void WindowPort::Fullscreen(bool g)
{
	FlushAnyText();
	WindowSystem::fullscreen= g;
	DevFullscreen(g);
}

void WindowPort::DevSetTitle(const char*)
{
}

//---- graphic functions -------------------------------------------------------

void WindowPort::Scroll(const Point &delta)
{
	Rectangle rl[4], r(cliprect), oldclip(cliprect);
	register int i, n;
	bool doscroll, batchopen= FALSE;

	FlushMyText();
	Rectangle r1(r + delta);
	doscroll= r.Clip(r1);

	n= Difference(rl, cliprect, r1);
	if (0 && n == 1 && WindowSystem::batch && !overlay) {
		UpdateRect(rl[0]);
		batchopen= TRUE;
	} else {
		for (i= 0; i < n; i++)
			InvalidateRect(rl[i]);
	}
	if (r.Clip(r1)) {
		if (batchopen)
			DevClip(oldclip);
		r.origin+= fso;
		DevScrollRect(r, delta);
	}
	if (batchopen)
		GiveHint(eHintUnbatch, 0, 0);
	DevUpdate();
}

bool WindowPort::DevImageCacheBegin(ImageCache *bb, const Rectangle &rr)
{
	Rectangle r(rr);
	if (WindowSystem::batch) {
		if (scale & 2) {
			DevClip(cliprect);
			scale&= ~2;
		}
		FlushMyText();
		r.origin += origin;
		if (r.Clip(cliprect)) {
			r.origin -= origin;
			bb->r= r;
			if (!bb->valid || !bb->vr.ContainsRect(r)) {
				bb->vr= r;
				if (bb->b == 0)
					bb->b= new Bitmap(r.extent, gDepth);
				return TRUE;
			}
		}
		r.origin += origin;
		batchInfo bi;
		bi.b= bb->b->GetDevBitmap();
		bi.r= r;
		bi.p= bb->r.origin - bb->vr.origin;
		DevGiveHint(eHintCacheStart, 0, &bi);
		return FALSE;
	}
	return TRUE;
}

void WindowPort::DevImageCacheEnd(ImageCache *bb)
{
	if (WindowSystem::batch) {
		if (scale & 2) {
			DevClip(cliprect);
			scale&= ~2;
		}
		batchInfo bi;
		bi.r= bb->vr;
		bi.r.origin += origin;
		if (bb->b == 0)
			bb->b= new Bitmap(bi.r.extent, gDepth);
		bi.b= bb->b->GetDevBitmap();
		DevGiveHint(eHintCacheEnd, 0, &bi);
		bb->valid= TRUE;
	}
}

//---- abstract methods --------------------------------------------------------

int WindowPort::DevGetId()
{
	AbstractMethod("DevGetId");
	return -1;
}

void WindowPort::DevSetCursor(GrCursor)
{
	AbstractMethod("DevSetCursor");
}

void WindowPort::DevScrollRect(const Rectangle&, const Point&)
{
	AbstractMethod("DevScrollRect");
}

void WindowPort::DevHide()
{
	AbstractMethod("DevHide");
}

void WindowPort::DevIconize()
{
	AbstractMethod("DevIconize");
}

void WindowPort::DevShow(WindowPort*, const Rectangle&)
{
	AbstractMethod("DevShow");
}

void WindowPort::DevFullscreen(bool)
{
	AbstractMethod("DevFullScreen");
}

void WindowPort::DevTop(bool)
{
	AbstractMethod("DevTop");
}

void WindowPort::DevSetMousePos(const Point&, bool)
{
	AbstractMethod("DevSetMousePos");
}

void WindowPort::DevSetColor2(u_long)
{
	AbstractMethod("DevSetColor2");
}

void WindowPort::DevBell(long duration)
{
	gWindowSystem->DevBell(duration);
}

Rectangle WindowPort::DevGetRect()
{
	return portrect;
}

void WindowPort::DevSetExtent(const Point &e)
{
	portrect.extent= e;
}

void WindowPort::DevSetOrigin(const Point &o)
{
	portrect.origin= o;
}

bool WindowPort::DragDropNotify(int code, const Point &globalpos, Rectangle *rp)
{
	if (win)
		return win->DragDropNotify(code, globalpos, rp);
	return 0;
}

